# encoding: utf-8
require "logstash/filters/base"
require "logstash/namespace"
require "/home/app/load-balance-request.jar"
java_import "com.sunrise.aliyun_request.MyMain"


class Message
  attr_reader :source, :transformed_message, :create_time, :host, :tags_str, :matched_error_prefix

  def initialize(host, source, transformed_message, tags_str, matched_error_prefix)
    @host,@source,@transformed_message,@tags_str,@matched_error_prefix = host,source,transformed_message,tags_str,matched_error_prefix
    @create_time = Time.new
  end

  def get_source
    return @source
  end
  def get_transformed_message
    return @transformed_message
  end
  def get_create_time
    return @create_time
  end
  def get_host
    return @host
  end
  def get_tags_str
    return @tags_str
  end
  def get_matched_error_prefix
    return @matched_error_prefix
  end
end

class EmailSender
  def send(host, source, transformed_message, tags_str, matched_error_prefix)
    from_user = '591593903@qq.com'
    to_user_list = ['591593903@qq.com', '365351930@qq.com', 'schling@shangyuan.cn']
    to_user_match_file_path = [nil, nil, nil]
    subject = '线上异常 ' + matched_error_prefix

    real_to_user_list = [to_user_list[0]]
    i = 1
    while i < to_user_list.size do
      if to_user_match_file_path[i].nil? || source =~ to_user_match_file_path[i]
        real_to_user_list.push(to_user_list[i])
      end
      i +=1
    end

    mail_body = <<-EOS
<b>正式环境 #{host} 发生异常</b>
<h1>日志文件 #{source}</h1>
<br/>
<h2>错误日志:</h2>
<div>#{transformed_message}</div>
    EOS

    if !tags_str.nil? && !tags_str.empty?
      mail_body = mail_body + '\n<br><h3>日志tags:</h3><div>' + tags_str + '</div>'
    end

    real_to_user_list_str = '['
    i=0
    while i < real_to_user_list.size do
      real_to_user_list_str = real_to_user_list_str + '"' + real_to_user_list[i] + '"'
      if i != real_to_user_list.size-1
        real_to_user_list_str = real_to_user_list_str + ','
      end
      i +=1
    end
    real_to_user_list_str = real_to_user_list_str + ']'

    params = Array["a", "SendMail", subject, mail_body, from_user, real_to_user_list_str]
    MyMain.main(params)
  end
end

class LogStash::Filters::LogListener < LogStash::Filters::Base
  config_name "logListener"

  config :error_logs, :validate =>:array, :default =>["com.dap.exception.PlatformException:", "com.dap.exception.RpcTimeoutException:", "java.lang.NullPointerException:"]


  private
  def fill_str(str,i=4)
    return str if str.size%i == 0
    str<<" "*(4-str.size%i)
  end
  private
  def txt_cmp(str_f0,str_f1)
    a0,a1 = str_f0.scan(/.{4}/m),str_f1.scan(/.{4}/m)
    n,m,s = a0.size,a1.size,0
    a0.each do |txt|
      if a1.include?(txt)
        size = a1.size
        s+=size-a1.keep_if {|item| item!=txt}.size
      end
      break if a1.size == 0
    end
    s/[n,m].max.to_f
  rescue =>e
    @logger.error("error : #{e.message}\n" << e.backtrace[0..2].join("\n"))
  end
  private
  def get_text_first_n_lines(t, line_count)
    i = 0
    c = 0
    while i<t.length do
      if t[i].ord == 10
        c += 1
        if c >= line_count
          break
        end
      end
      i += 1
    end

    if c >= line_count
      return t.slice(0, i)
    else
      return nil
    end
  end
  private
  def java_txt_similar(a,b)
    a10 = get_text_first_n_lines(a, 10)
    b10 = get_text_first_n_lines(b, 10)
    if !a10.nil? and !b10.nil?
      return txt_cmp(a10,b10)
    elsif a10.nil? and b10.nil?
      return txt_cmp(a,b)
    else
      return 0.0
    end
  end
  private
  def remove_too_old_send_message()
    oldest_time = Time.new.to_i - 180
    i = 0
    send_message_count = @send_messages.size
    while i < send_message_count do
      sme = @send_messages[i]
      if sme.get_create_time.to_i > oldest_time
        break
      end
      i +=1
    end
    if i < send_message_count
      @send_messages = @send_messages.slice(i, send_message_count-i)
    else
      @send_messages = Array.new()
    end
  end
  private
  def filter_send_mail(messageElement)
    if @send_messages.size >= 10
      return true
    end

    i = 0
    send_message_count = @send_messages.size
    while i < send_message_count do
      sme = @send_messages[i]
      if java_txt_similar(sme.get_transformed_message, messageElement.get_transformed_message) >= 0.7
        return true
      end
      i +=1
    end

    @send_messages << messageElement

    return false
  end

  private
  def send_mail(element)
    host = element.get_host
    if (host.nil?)
      host = ''
    end

    source = element.get_source
    if (source.nil?)
      source = ''
    end

    transformed_message = element.get_transformed_message
    if (transformed_message.nil?)
      transformed_message = ''
    end

    tags_str = element.get_tags_str
    if (tags_str.nil?)
      tags_str = ''
    end

    matched_error_prefix = element.get_matched_error_prefix

    @logger.error("will send mail, transformed_message = " + transformed_message)
    EmailSender.new().send(host, source, transformed_message, tags_str, matched_error_prefix)
  end

  public
  def register
    if defined?(LogStash::Logger) && LogStash::Logger.respond_to?(:setup_log4j)
      LogStash::Logger.setup_log4j(@logger)
    end

    @queue = Queue.new
    @send_messages = Array.new()
    Thread.new {
      while(true)
        messageElement = @queue.deq
        remove_too_old_send_message()
        if (!filter_send_mail(messageElement))
          send_mail(messageElement)
        end
      end
    }
  end



  private
  def transform_message_to_html_text(message)
    message = message.gsub("|","")
    message = message.gsub(" ","&nbsp;")
    message = message.gsub("?","")
    message = message.gsub(">","&gt;")
    message = message.gsub("<","&lt;")
    message = message.gsub("\"","&quot;")
    message = message.gsub("\'","&quot;")
    message = message.gsub("\n","<br>")
    return message
  end

  private
  def handle_error_message(event, message, matched_error_prefix)
    host = event.get("host")
    if host.nil?
      event.set("host", "没有找到服务器")
    end

    source = event.get("source")
    if source.nil?
      event.set("source", "没有找到源文件")
    end

    transformed_message = transform_message_to_html_text(message)

    tags = event.get("tags")
    if tags.nil?
      tags = []
    end
    tags_str = ''
    i=0
    while i < tags.size
      tags_str = tags_str + tags[i] + '<br>'
      i +=1
    end
    event.set("tags_str", tags_str)

    messageElement = Message.new(host, source, transformed_message, tags_str, matched_error_prefix)
    @queue.enq(messageElement)

  end

  private
  def get_matched_error_prefix(message)
    i = 0
    error_logs_count = error_logs.size
    while i < error_logs_count do
      if message.include?error_logs[i]
        return error_logs[i]
      end
      i +=1
    end
    return nil
  end

  private
  def parse_message(message)
    return message.split(" | ")
  end
  private
  def is_worker_main_query(message_parts)
    if message_parts.nil? or message_parts.size < 4
      return false
    end

    thread_name = message_parts[2]
    message_body = message_parts[message_parts.size-1]
    if thread_name.include?"Worker" or thread_name.include?"Job"
      if message_body.include?"slow sql"
        return true
      end
    end

    return false
  end

  public
  def filter(event)
    message = event.get("message")
    if message.blank?
      return
    end

    matched_error_prefix = get_matched_error_prefix(message)
    if !matched_error_prefix.nil? and !is_worker_main_query(parse_message(message))
      handle_error_message(event, message, matched_error_prefix)
    end

    filter_matched(event)
  end

end
